/**
 * @file tcp_server_app_transport.c
 * @brief Transport protocol abstraction layer
 * @Author: aple
 * @Date: 2025-9-27 9:20:10
 * @LastEditors: aple
 * @LastEditTime: 2025-9-27 18:37:50
 * @Verision: 1.0.0.0
 **/

// Switch to the appropriate trace level
#define TRACE_LEVEL TCP_APP_TRACE_LEVEL

// Dependencies
#include "core/net.h"
#include "server/tcp_server_app.h"
#include "server/tcp_server_app_transport.h"
#include "debug.h"

/**
 * @brief Accept connection request
 * @param[in] context Pointer to the TcpApp/TCP server context
 **/

void TcpAppServerAcceptConnection(TcpAppServerContext *context)
{
   error_t error;
   uint_t i;
   Socket *socket;
   IpAddr clientIpAddr;
   uint16_t clientPort;
   TcpAppClientConnection *connection;

   // Accept incoming connection
   socket = socketAccept(context->socket, &clientIpAddr, &clientPort);

   // Make sure the socket handle is valid
   if (socket != NULL)
   {
      // Force the socket to operate in non-blocking mode
      socketSetTimeout(socket, 0);

      // Initialize pointer
      connection = NULL;

      // Loop through the connection table
      for (i = 0; i < TCP_APP_SERVER_MAX_CONNECTIONS; i++)
      {
         // Check the state of the current connection
         if (context->connection[i].state == TCP_APP_CONNECTION_STATE_CLOSED)
         {
            // The current entry is free
            connection = &context->connection[i];
            break;
         }
      }

      // If the connection table runs out of space, then the client's connection
      // request is rejected
      if (connection != NULL)
      {
         // Debug message
         TRACE_INFO("TcpApp Server: Connection established with client %s port %" PRIu16 "...\r\n", ipAddrToString(&clientIpAddr, NULL), clientPort);

         // Clear the structure describing the connection
         osMemset(connection, 0, sizeof(TcpAppClientConnection));

         // Attach TcpApp/TCP server context
         connection->context = context;
         // Save socket handle
         connection->socket = socket;
         // Initialize time stamp
         connection->timestamp = osGetSystemTime();

         // Any registered callback?
         if (context->settings.openCallback != NULL)
         {
            // Invoke callback function
            error = context->settings.openCallback(connection, clientIpAddr,
                                                   clientPort);
         }
         else
         {
            // No callback function registered
            error = NO_ERROR;
         }

         // Check status code
         if (!error)
         {
            // Wait for incoming TcpApp requests
            connection->state = TCP_APP_CONNECTION_STATE_RECEIVE;
         }
         else
         {
            // Reject the incoming connection request
            TcpAppServerCloseConnection(connection);
         }
      }
      else
      {
         // Debug message
         TRACE_INFO("TcpApp Server: Connection refused with client %s port %" PRIu16 "...\r\n", ipAddrToString(&clientIpAddr, NULL), clientPort);

         // The TcpApp/TCP server cannot accept the incoming connection request
         socketClose(socket);
      }
   }
}

/**
 * @brief Shutdown network connection
 * @param[in] connection Pointer to the client connection
 * @return Error code
 **/

error_t TcpAppServerShutdownConnection(TcpAppClientConnection *connection)
{
   error_t error;

   // Initialize status code
   error = NO_ERROR;

   // Check the state of the connection
   if (connection->state == TCP_APP_CONNECTION_STATE_RECEIVE)
   {
      // Shutdown transmission
      error = socketShutdown(connection->socket, SOCKET_SD_SEND);
      // Update connection state
      connection->state = TCP_APP_CONNECTION_STATE_SHUTDOWN_TX;
   }
   else if (connection->state == TCP_APP_CONNECTION_STATE_SHUTDOWN_TX)
   {
      // Shutdown reception
      error = socketShutdown(connection->socket, SOCKET_SD_RECEIVE);
      // Update connection state
      connection->state = TCP_APP_CONNECTION_STATE_SHUTDOWN_RX;
   }
   else
   {
      // Invalid state
      error = ERROR_WRONG_STATE;
   }

   // Return status code
   return error;
}

/**
 * @brief Close network connection
 * @param[in] connection Pointer to the client connection
 **/

void TcpAppServerCloseConnection(TcpAppClientConnection *connection)
{
   TcpAppServerContext *context;

   // Debug message
   TRACE_INFO("TcpApp Server: Closing connection...\r\n");

   // Point to the TcpApp/TCP server context
   context = connection->context;

   // Close TCP connection
   if (connection->socket != NULL)
   {
      socketClose(connection->socket);
      connection->socket = NULL;
   }

   // Any registered callback?
   if (context->settings.closeCallback != NULL)
   {
      // Invoke callback function
      context->settings.closeCallback(connection);
   }

   // Mark the connection as closed
   connection->state = TCP_APP_CONNECTION_STATE_CLOSED;
}

/**
 * @brief Send data using the relevant transport protocol
 * @param[in] connection Pointer to the client connection
 * @param[in] data Pointer to a buffer containing the data to be transmitted
 * @param[in] length Number of bytes to be transmitted
 * @param[out] written Actual number of bytes written (optional parameter)
 * @param[in] flags Set of flags that influences the behavior of this function
 * @return Error code
 **/

error_t TcpAppServerSendData(TcpAppClientConnection *connection,
                             const void *data, size_t length, size_t *written, uint_t flags)
{
   error_t error;

   // Transmit data
   error = socketSend(connection->socket, data, length, written, flags);

   // Return status code
   return error;
}

/**
 * @brief Receive data using the relevant transport protocol
 * @param[in] connection Pointer to the client connection
 * @param[out] data Buffer into which received data will be placed
 * @param[in] size Maximum number of bytes that can be received
 * @param[out] received Number of bytes that have been received
 * @param[in] flags Set of flags that influences the behavior of this function
 * @return Error code
 **/

error_t TcpAppServerReceiveData(TcpAppClientConnection *connection,
                                void *data, size_t size, size_t *received, uint_t flags)
{
   error_t error;

   // Receive data
   error = socketReceive(connection->socket, data, size, received, flags);

   // Return status code
   return error;
}

